#pragma once

#include <nanobdd/common.h>
#include <nanobdd/node.h>

#include <forward_list>
#include <list>
#include <map>
#include <optional>
#include <tuple>
#include <vector>

namespace nanobdd {

class ListApplyCache {
 public:
  ListApplyCache() = default;
  ListApplyCache(size_t size) : size_(size) {
    cache_.resize(size);
  }

  struct CacheEntry {
    Node* node;
    Node* left;
    Node* right;
    uint32_t op;
  };

  void
  insert(
      uint32_t hash,
      Node* node,
      uint32_t op,
      Node* left,
      Node* right) {
    
    auto& entry = cache_.at(hash);
    CacheEntry ce;
    ce.node = node;
    ce.left = left;
    ce.right = right;
    ce.op = op;
    entry.push_front(std::move(ce));
  }

  Node*
  // std::optional<std::shared_ptr<Node>>
  lookup(uint32_t hash, uint32_t op, Node* left, Node* right) {
    

    auto& entry = cache_.at(hash);
    for (auto& e : entry) {
      if (e.op == op && e.left == left && e.right == right) {
        return e.node;
      }
    }

    // return std::nullopt;
    return nullptr;
  }

  void
  resize(size_t size) {
    size_ = size;
    cache_.resize(size);
  }

  size_t
  size() {
    return cache_.size();
  }

  auto&
  cache() {
    return cache_;
  }

 private:
  uint32_t size_;
  std::vector<std::list<CacheEntry>> cache_;
};

} // namespace nanobdd